/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.common.base;

import com.alibaba.fastjson2.JSONObject;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.je.common.base.service.rpc.BeanService;
import com.je.common.base.spring.SpringContextHolder;
import com.je.common.base.util.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 自定义动态类
 * <p>
 * 手工设定属性的内容，系统保留以下属性：
 * $TABLE_CODE$ :   表的名称
 * $ROWS$       :   结果记录集
 * $P_COUNT$      :   当前返回记录数
 * $A_COUNT$  :   总的查询记录数
 * $SQL$        :   查询的SQL语句
 * .....
 *
 * @author wangmm@ketr.com.cn
 * @date 2019/12/11
 */
public class DynaBean implements Serializable {

    /**
     * 序列号
     */
    private static final long serialVersionUID = -2563215753689331432L;

    private static final Logger logger = LoggerFactory.getLogger(DynaBean.class);
    /**
     * 存放属性的值集
     */
    private HashMap<String, Object> values = new HashMap<String, Object>();
    /**
     * 默认租户ID字段
     */
    private String tenantIdField = "SY_TENANT_ID";
    /**
     * 默认租户名称字段
     */
    private String tenantNameField = "SY_TENANT_NAME";

    public String getTenantIdField() {
        return tenantIdField;
    }

    public void setTenantIdField(String tenantIdField) {
        this.tenantIdField = tenantIdField;
    }

    public String getTenantNameField() {
        return tenantNameField;
    }

    public void setTenantNameField(String tenantNameField) {
        this.tenantNameField = tenantNameField;
    }

    /**
     * 初始化空的动态类
     */
    public DynaBean() {
        this.set(BeanService.KEY_WHERE, "");
    }


    private BeanService getBeanService() {
        return SpringContextHolder.getBean(BeanService.class);
    }

    /**
     * 初始化带表编码信息的动态类
     * 不被推荐的方法
     *
     * @param tableCode 表的编码（即数据库表名）
     */
    @Deprecated
    public DynaBean(String tableCode) {
        values.put(BeanService.KEY_TABLE_CODE, tableCode);
        //初始化主键字段
        String pkName = getBeanService().getPKeyFieldNamesByTableCode(tableCode);
        if (pkName != null) {
            this.set(BeanService.KEY_PK_CODE, pkName);
        }
        this.set(BeanService.KEY_WHERE, "");
    }

    /**
     * 初始化带表编码信息的动态类(是否装载组建)
     *
     * @param tableCode 表的编码（即数据库表名）
     * @param isHavePK  是否自动生成主键的名称
     */
    public DynaBean(String tableCode, Boolean isHavePK) {
        values.put(BeanService.KEY_TABLE_CODE, tableCode);
        if (isHavePK) {
            String pkName = getBeanService().getPKeyFieldNamesByTableCode(tableCode);
            if (pkName != null) {
                this.set(BeanService.KEY_PK_CODE, pkName);
            }
        }
        this.set(BeanService.KEY_WHERE, "");
    }

    /**
     * 研发部:云凤程
     * 初始化带表编码信息的动态类
     *
     * @param tableCode 表的编码（即数据库表名）
     * @param isHavePK  是否自动生成主键的名称
     * @param all       是否装载字段数据
     */
    public DynaBean(String tableCode, Boolean isHavePK, Boolean all) {
        values.put(BeanService.KEY_TABLE_CODE, tableCode);
        if (isHavePK) {
            String pkName = getBeanService().getPKeyFieldNamesByTableCode(tableCode);
            if (pkName != null) {
                this.set(BeanService.KEY_PK_CODE, pkName);
            }
        }
        if (all) {
            List<DynaBean> columns = getBeanService().getResourceTable(tableCode).getDynaBeanList(BeanService.KEY_TABLE_COLUMNS);
            for (DynaBean c : columns) {
                this.set(c.getStr("TABLECOLUMN_CODE"), "");
            }
        }
        this.set(BeanService.KEY_WHERE, "");
    }

    //--------------------------------------------------特殊参数

    /**
     * 设置表名
     *
     * @param tableCode 表名
     * @return com.je.core.util.bean.DynaBean
     */
    public DynaBean table(String tableCode) {
        values.put(BeanService.KEY_TABLE_CODE, tableCode);
        //初始化主键字段
        String pkName = getBeanService().getPKeyFieldNamesByTableCode(tableCode);
        if (pkName != null) {
            this.set(BeanService.KEY_PK_CODE, pkName);
        }
        return this;
    }

    public DynaBean getDynaBean(String field) {
        Object result = get(field);
        if (result instanceof HashMap) {
            DynaBean newDynaBean = new DynaBean();
            newDynaBean.setValues((HashMap) ((HashMap) result).get("values"));
            return newDynaBean;
        }
        return (DynaBean) result;
    }

    public List<DynaBean> getDynaBeanList(String field) {
        if (get(field) == null) {
            return new ArrayList<>();
        }
        List result = (List) get(field);
        if (result == null) {
            return new ArrayList<>();
        }
        List<DynaBean> resultList = Lists.newArrayList();
        result.forEach(each -> {
            if (each instanceof HashMap) {
                DynaBean newDynaBean = new DynaBean();
                newDynaBean.setValues((HashMap) ((HashMap) each).get("values"));
                resultList.add(newDynaBean);
            } else {
                resultList.add((DynaBean) each);
            }
        });
        return resultList;
    }

    /**
     * 获取表名
     *
     * @return java.lang.String
     */
    public String getTableCode() {
        return getStr(BeanService.KEY_TABLE_CODE);
    }

    /**
     * 设置主键字段名
     *
     * @param pkCode 主键字段名
     * @return com.je.core.util.bean.DynaBean
     */
    public DynaBean pkCode(String pkCode) {
        values.put(BeanService.KEY_PK_CODE, pkCode);
        return this;
    }

    /**
     * 获取主键字段名
     *
     * @return java.lang.String
     */
    public String getPkCode() {
        return getStr(BeanService.KEY_PK_CODE);
    }

    /**
     * 获取主键值
     *
     * @return java.lang.String
     */
    public String getPkValue() {
        return getStr(getPkCode());
    }

    //--------------------------------------------------put

    /**
     * 存放值
     *
     * @param key   键
     * @param value 值
     * @return java.lang.Object
     */
    public Object put(String key, Object value) {
        //英文+数字+下划线
        String columnRegex = "^\\w+$";
        //校验是否匹配规则
        if (key != null && key.trim().matches(columnRegex)) {
            key = key.trim();
        } else {
//            logger.error("column '{}' is invalid.", key);
        }
        // 原逻辑
        if (!Strings.isNullOrEmpty(key)) {
            if (BeanService.KEY_TABLE_CODE.equals(key)) {
                values.put(key, value);
                //如果传入的是表CODE继续进行主键操作
                //如果已经有PK_CODE则不去查询
                if (!(this.get(BeanService.KEY_PK_CODE) != null && !this.get(BeanService.KEY_PK_CODE).equals(""))) {
                    String pkName = getBeanService().getPKeyFieldNamesByTableCode((String) value);
                    if (pkName != null) {
                        this.set(BeanService.KEY_PK_CODE, pkName);
                    }
                }
            } else if (BeanService.KEY_WHERE.equals(key)) {
                values.put(key, value);
            } else {
                values.put(key, value);
            }
        }
        return null;
    }


    /**
     * 设置属性值，如果有则直接覆盖，如果没有则添加一个
     *
     * @param key   属性名称
     * @param value 属性值
     */
    public DynaBean set(String key, Object value) {
        put(key, value);
        return this;
    }

    /**
     * 设置String类型的属性值，如果有则直接覆盖，如果没有则添加一个
     *
     * @param key   属性名称
     * @param value 属性值
     */
    public DynaBean setStr(String key, String value) {
        put(key, value);
        return this;
    }

    /**
     * 适用于枚举
     * 设置String类型的属性值，如果有则直接覆盖，如果没有则添加一个
     *
     * @param key   属性名称
     * @param value 属性值
     */
    public DynaBean setStr(Enum<?> key, String value) {
        return setStr(key.toString(), value);
    }

    /**
     * 设置int类型的属性值，如果有则直接覆盖，如果没有则添加一个
     *
     * @param key   属性名称
     * @param value 属性值
     */
    public DynaBean setInt(String key, int value) {
        put(key, value);
        return this;
    }

    /**
     * 设置long类型的属性值，如果有则直接覆盖，如果没有则添加一个
     *
     * @param key   属性名称
     * @param value 属性值
     */
    public DynaBean setLong(String key, long value) {
        put(key, value);
        return this;
    }

    /**
     * 设置float类型的属性值，如果有则直接覆盖，如果没有则添加一个
     *
     * @param key   属性名称
     * @param value 属性值
     */
    public DynaBean setFloat(String key, float value) {
        put(key, value);
        return this;
    }

    /**
     * 设置double类型的属性值，如果有则直接覆盖，如果没有则添加一个
     *
     * @param key   属性名称
     * @param value 属性值
     */
    public DynaBean setDouble(String key, double value) {
        put(key, value);
        return this;
    }

    /**
     * 根据表字段设置值
     *
     * @param jsonObj json数据
     */
    public void setJsonValues(JSONObject jsonObj) {
        setBeanValues(jsonObj, new String[]{});
    }

    /**
     * 根据表字段设置值
     *
     * @param valueMap 值
     */
    public void setBeanValues(Map<String, Object> valueMap) {
        setBeanValues(valueMap, new String[]{});
    }

    /**
     * 根据表字段设置值
     *
     * @param valueMap 值
     * @param excludes 排除字段
     */
    public void setBeanValues(Map<String, Object> valueMap, String[] excludes) {
        //1.得到表结构s
        String tabelCode = (String) values.get(BeanService.KEY_TABLE_CODE);
        DynaBean table = getBeanService().getResourceTable(tabelCode);
        //2.得到列模式
        List<DynaBean> columns = table.getDynaBeanList(BeanService.KEY_TABLE_COLUMNS);
        for (DynaBean column : columns) {
            String fieldCode = column.getStr("TABLECOLUMN_CODE");
            if (ArrayUtils.contains(excludes, fieldCode)) {
                continue;
            }
            if (valueMap.containsKey(fieldCode)) {
                if (valueMap.get(fieldCode) == null || "null".equals(valueMap.get(fieldCode).toString())
                        || "[]".equals(valueMap.get(fieldCode).toString())
                        || Strings.isNullOrEmpty(valueMap.get(fieldCode).toString())) {
                    values.put(fieldCode, null);
                } else {
                    values.put(fieldCode, valueMap.get(fieldCode));
                }
            }
        }
    }

    /**
     * 获取字段值
     *
     * @param column 字段名
     * @return 字段值
     */
    public Object get(String column) {
        return get(column, null);
    }

    //--------------------------------------------------get

    /**
     * 获取字段值
     *
     * @param column       字段名
     * @param defaultValue 字段默认值
     * @return 字段值
     */
    public Object get(String column, Object defaultValue) {
        return values.getOrDefault(column, defaultValue);
    }

    /**
     * 获取字段值 字符串
     *
     * @param column 字段名
     * @return 字段值
     */
    public String getStr(String column) {
        return getStr(column, null);
    }

    /**
     * 获取字段值 字符串
     *
     * @param column       字段名
     * @param defaultValue 字段默认值
     * @return 字段值
     */
    public String getStr(String column, String defaultValue) {
        Object value = get(column, defaultValue);
        if (value == null) {
            return defaultValue;
        }
        return String.valueOf(value);
    }

    /**
     * 获取字段值 Integer
     *
     * @param column 字段名
     * @return 字段值
     */
    public Integer getInt(String column) {
        return getInt(column, 0);
    }

    /**
     * 获取字段值 Integer
     *
     * @param column       字段名
     * @param defaultValue 字段默认值
     * @return 字段值
     */
    public Integer getInt(String column, int defaultValue) {
        String value = getStr(column);
        if (value == null || value.length() == 0) {
            return defaultValue;
        } else {
            return Integer.parseInt(value);
        }
    }

    /**
     * 获取字段值 Long
     *
     * @param column 字段名
     * @return 字段值
     */
    public Long getLong(String column) {
        return getLong(column, 0);
    }

    /**
     * 获取字段值 Long
     *
     * @param column       字段名
     * @param defaultValue 字段默认值
     * @return 字段值
     */
    public Long getLong(String column, long defaultValue) {
        String value = getStr(column);
        if (value == null || value.length() == 0) {
            return defaultValue;
        } else {
            return Long.parseLong(value);
        }
    }

    /**
     * 获取字段值 Double
     *
     * @param column 字段名
     * @return 字段值
     */
    public Double getDouble(String column) {
        return getDouble(column, 0);
    }

    /**
     * 获取字段值 Double
     *
     * @param column       字段名
     * @param defaultValue 字段默认值
     * @return 字段值
     */
    public Double getDouble(String column, double defaultValue) {
        String value = getStr(column);
        if (value == null || value.length() == 0) {
            return defaultValue;
        } else {
            return Double.parseDouble(value);
        }
    }

    /**
     * 获取字段值 Double
     *
     * @param column 字段名
     * @return 字段值
     */
    public Float getFloat(String column) {
        return getFloat(column, 0);
    }

    /**
     * 获取字段值 Float
     *
     * @param column       字段名
     * @param defaultValue 字段默认值
     * @return 字段值
     */
    public Float getFloat(String column, float defaultValue) {
        String value = getStr(column);
        if (value == null || value.length() == 0) {
            return defaultValue;
        } else {
            return Float.parseFloat(value);
        }
    }

    /**
     * 得到值集
     *
     * @return 值集
     */
    public HashMap<String, Object> getValues() {
        values.remove(BeanService.KEY_TABLE_CODE);
        values.remove(BeanService.KEY_PK_CODE);
        values.remove(BeanService.KEY_WHERE);
        values.remove(BeanService.DEF_ALL_FIELDS);
        values.remove(BeanService.KEY_QUERY_FIELDS);
        values.remove(BeanService.KEY_ORDER);
        return values;
    }

    /**
     * 设置值集
     *
     * @param valueMap 值集
     */
    public void setValues(Map<String, Object> valueMap) {
        this.values.clear();
        this.values.putAll(valueMap);
    }

    /**
     * 得到值集
     *
     * @return 值集
     */
    public HashMap<String, Object> fetchFilterValues() {
        HashMap<String, Object> hashmap = (HashMap) values.clone();
        hashmap.remove(BeanService.KEY_TABLE_CODE);
        hashmap.remove(BeanService.KEY_PK_CODE);
        hashmap.remove(BeanService.KEY_WHERE);
        hashmap.remove(BeanService.DEF_ALL_FIELDS);
        hashmap.remove(BeanService.KEY_QUERY_FIELDS);
        hashmap.remove(BeanService.KEY_ORDER);
        return hashmap;
    }

    /**
     * 获取当前全部数据包含TableCode等
     *
     * @return java.util.HashMap
     */
    public HashMap<String, Object> fetchAllValues() {
        return values;
    }

    //--------------------------------------------------other

    /**
     * 清除数据
     */
    public void clear() {
        this.values.clear();
    }

    /**
     * 覆盖父对象的clone方法，复制出一份内容完全一样的新对象。
     *
     * @return 内容完全一样的新对象
     */
    @Override
    public DynaBean clone() {
        DynaBean dynaBean = new DynaBean();
        dynaBean.setValues((HashMap) values.clone());
        return dynaBean;
    }

    /**
     * 删除指定属性
     *
     * @param key key
     */
    public void remove(String key) {
        if (containsKey(key)) {
            values.remove(key);
        }
    }

    /**
     * 是否包含键
     *
     * @param key key
     */
    public Boolean containsKey(String key) {
        return values.containsKey(key);
    }
}
